#include <QtTest/QtTest>
#include "optManager.h"

int verboseLevel=0;
QString console="";

void print(QString data)
{
   console += data + "\n";
}

void printIntro(const QStringList& actualAppArgList)
{
   print ("Welcome to test1\n You passed in the following arguments");
   for (int i = 0; i<actualAppArgList.size(); ++i)
      print (actualAppArgList[i]);
}

void verbose(const OptSwitch * opt)
{
   QStringList arg = opt->argList();
   int level;
   bool ok = true;
   if (arg.isEmpty())
      level = 1;
   else
      level = arg[0].toInt(&ok,10);
   if (!ok)
      level = 1;
   verboseLevel = level;
}

void verboseAction()
{
   OptManager * om = OptManager::getOptMgr();
   if (verboseLevel & 1)
      print(om->appArgDump());
   if (verboseLevel & 2)
      print(om->switchDump());
}

void echo(const OptSwitch * opt)
{
   QStringList args = opt->argList();
   QString TEXT;
   if (args.isEmpty())
      TEXT = "No details provided -- should never get here.";
   else
      TEXT = args[0];
   print(TEXT);
}

class qgetoptTest: public QObject
{
   Q_OBJECT
   private:
      OptManager * om;
      QString appName;
      QString testFile;
   private slots:
      void blackBox();
      void initTestCase();
      void appNameTest();
      void addAppArgsTest();
      void addDescriptionTest();
      void addLongSwitch();
      void addShortSwitch();
      void addSwitchWithArguments();
      void addSwitchWithOptionalArgs();
      void addDuplicateSwitch();
      void checkSwitchUsed();
   private:
      void checkFiles(QString filename1,QString filename2);
      void generateCoverageFiles();
      // Clear all previous unit tests and create a simple test program
      void initAppTestCases(bool init = false);
};

void qgetoptTest::checkFiles(QString filename1,QString filename2)
{
   QFile actual(filename1);
   QFile expected("../srcTest2/expected/"+filename2);
   actual.open(QFile::ReadOnly);
   expected.open(QFile::ReadOnly);
   char actualLine[160];
   char expectedLine[160];
   while (!actual.atEnd() && !expected.atEnd())
   {
      actual.readLine(actualLine,160);
      expected.readLine(expectedLine,160);
      QCOMPARE(actualLine,expectedLine);
   }
   QCOMPARE(actual.atEnd(),expected.atEnd());
}

void qgetoptTest::generateCoverageFiles()
{
   QProcess *lcov_Process = new QProcess(this);
   lcov_Process->start("lcov -c  -d ./ -o ../lcov_tmp/qgetopt_test.info -t blackBoxTests");
   lcov_Process->waitForFinished();
   lcov_Process->start("lcov -e ../lcov_tmp/qgetopt_test.info \"*optionManager/opt*\" -o ../lcov_tmp/qgetopt_test.info");
   lcov_Process->waitForFinished();
   lcov_Process->start("lcov -a ../lcov_tmp/qgetopt_test.info -a ../lcov_tmp/qgetopt_total_coverage.info -o ../lcov_tmp/qgetopt_total_coverage.info");
   lcov_Process->waitForFinished();
   lcov_Process->start("lcov -z -d ./");
   lcov_Process->waitForFinished();
}

void qgetoptTest::initTestCase()
{
   appName = "Test 2";
   om = OptManager::getOptMgr(appName);
   testFile = "./test1 ";
}

void qgetoptTest::appNameTest()
{
   QCOMPARE(appName, om->getAppName());
}

void qgetoptTest::addAppArgsTest()
{
   QString caughtError = "";
   om->addAppArgs("FILE    REPLACE");
   QCOMPARE(om->getAppArgTemplate(0),QString("FILE"));
   QCOMPARE(om->getAppArgTemplate(1),QString("REPLACE"));
   QCOMPARE(om->getAppArgTemplate(2),QString(""));
   om->addAppArgs("DATE");
   QCOMPARE(om->getAppArgTemplate(2),QString("DATE"));
   try
   {
      om->addAppArgs("[ERROR]");
   }
   catch (QString &e)
   {
      caughtError = e;
   }
   QCOMPARE(caughtError,QObject::tr("Program arguments cannot be optional"));
   QCOMPARE(om->getAppArgTemplate(3),QString(""));
}

void qgetoptTest::addDescriptionTest()
{
   QString desc = QObject::tr("This is a description of the application");
   om->addDescription(desc);
   QCOMPARE(om->getAppDesc(),desc);
}

void qgetoptTest::addLongSwitch()
{
   QString detail = QObject::tr("This is the description for a switch1.");
   om->addSwitch("switch1",0)->addDescription(detail);
   QCOMPARE(om->switchDumpTemplate(1).simplified(),"--switch1 "+detail);
}

void qgetoptTest::addShortSwitch()
{
   QString detail = QObject::tr("This is the description for a switch2.");
   om->addSwitch("switch2",0,'2')->addDescription(detail);
   QCOMPARE(om->switchDumpTemplate(2).simplified(),"-2 --switch2 "+detail);
}

void qgetoptTest::addSwitchWithArguments()
{
   QString detail = QObject::tr("This is the description for a switch3.");
   QString args = QObject::tr("arg1 arg2");
   om->addSwitch("switch3",0,'3',args)->addDescription(detail);
   QCOMPARE(om->switchDumpTemplate(3).simplified(),"-3 --switch3 "+args+" "+detail);
}

void qgetoptTest::addSwitchWithOptionalArgs()
{
   // Add an optional argument at the end
   QString detail = QObject::tr("This is the description for a switch4.");
   QString args = QObject::tr("arg1 arg2 [arg3]");
   om->addSwitch("switch4",0,'4',args)->addDescription(detail);
   QCOMPARE(om->switchDumpTemplate(4).simplified(),"-4 --switch4 "+args+" "+detail);

   // Add an optional argument at the start, but it should still appear at the end
   detail = QObject::tr("This is the description for a switch5.");
   om->addSwitch("switch5",0,'5',QObject::tr("[arg3] arg1 arg2"))->addDescription(detail);
   QCOMPARE(om->switchDumpTemplate(5).simplified(),"-5 --switch5 "+args+" "+detail);
}

 void qgetoptTest::addDuplicateSwitch()
 {
   QString caughtError = "";
   om->addSwitch("switch6",0,'6');
   try
   {
      om->addSwitch("switch6",0,'6');
   }
   catch (QString &e)
   {
      caughtError = e;
   }
   QCOMPARE(caughtError,QObject::tr("Duplicate switch -6 used."));
 }


 void qgetoptTest::initAppTestCases(bool init)
 {
   OptManager::clear();
   QString omappName = "AppTest 2";

   om = OptManager::getOptMgr(omappName);

   QString detail;
   om->addAppArgs("FILE");
   detail = QObject::tr("This program is a test program for the \"qgetopt\" library. It will accept various command line options and requires the name of a file to be passed in.");
   om->addDescription(detail);
   if (init)
      om->addInitMethod(printIntro);
   detail = QObject::tr("This is the level of detail that will be written, by default to the console but it can also be written to the file. 0 no detail. 1 the program argument passed in. 2 the options and their value. Add 4 to any of the previous levels and the details will also be written to the file.");
   om->addSwitch("verbose",verbose, 'v', "[LEVEL]",verboseAction)->addDescription(detail);
   detail = QObject::tr("This will echo TEXT to the console.");
   om->addSwitch("echo",echo, ' ', "TEXT",0,immediateStop)->addDescription(detail);
}

 void qgetoptTest::checkSwitchUsed()
 {
    char progName[] = "test2";
    char arg1error[] = "verbose";
    char arg1[] = "--verbose";
    char arg2[] = "7";
    char progArg[] = "test2.log";
    char * arglist1[] = {progName,progArg};
    char * arglist2[] = {progName,arg1error,arg2,progArg};
    char * arglist3[] = {progName,arg1,arg2,progArg};
    initAppTestCases();
    om->parse(2,arglist1);
    QCOMPARE(om->getSwitchDetails("verbose"),QStringList());
    initAppTestCases(true);
    om->parse(2,arglist1);
    QCOMPARE(console.simplified(),QString("Welcome to test1 You passed in the following arguments test2.log"));
    initAppTestCases();
    QCOMPARE(om->parse(4,arglist2,true).simplified(),QString("Error: No switch defined for the argument verbose."));
    initAppTestCases();
    om->parse(4,arglist3);
    QCOMPARE(om->getSwitchDetails("verbose"),QStringList(QString("7")));
 }

void qgetoptTest::blackBox()
{
   /* Black box testing:

      Scenarios
      =========

      Successful Tests:
      * Terminating short switch
      * Terminating long switch
      * Terminating switch with argument
      * Program argument
      * Switch with argument and program argument
      * Two switches one with an argument and program argument
      * Two switches one one short one long with an argument and program argument

      Error Tests:
      * Invalid switch
      * program argument missing
      * Switch with argument missing
      * Switch with too many arguments
      * Duplicate switch
   */
   QProcess *test_QGetOpt_Process = new QProcess(this);
   test_QGetOpt_Process->setStandardOutputFile("test_QGetOpt_Process.actual");
   test_QGetOpt_Process->start(testFile+" -h");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-h.expected");
   test_QGetOpt_Process->start(testFile+" --help");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-h.expected");
   test_QGetOpt_Process->start(testFile+" --echo \"This is a line of echoed text.\"");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process--echo.expected");
   test_QGetOpt_Process->start(testFile+" -v 7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-v7.expected");
   test_QGetOpt_Process->start(testFile+" -v 6 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-v6.expected");
   test_QGetOpt_Process->start(testFile+" -v 5 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-v5.expected");
   test_QGetOpt_Process->start(testFile+" -dv 7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-dv7.expected");
   test_QGetOpt_Process->start(testFile+" -d --verbose 7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-d--verbose7.expected");
   test_QGetOpt_Process->start(testFile+" --echo 3.1415926573");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process--echo-decimal.expected");


   // Check error states
   // Unknown short switch
   test_QGetOpt_Process->start(testFile+" -dv7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-dv7-error.expected");
   // Unknown long switch
   test_QGetOpt_Process->start(testFile+" --errorSwitch test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-errorSwitch.expected");
   test_QGetOpt_Process->start(testFile);
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-error.expected");
   test_QGetOpt_Process->start(testFile+" --echo");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process--echo-error.expected");
   // Too many arguments no more switches to process
   test_QGetOpt_Process->start(testFile+" --verbose 7 4 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-error-too-many-arguments.expected");
   // Too many arguments with more switches to process
   test_QGetOpt_Process->start(testFile+" --verbose 7 4 -d test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-error-too-many-arguments.expected");
   // short switch (b --backup FILE) missing a compulsory argument
   test_QGetOpt_Process->start(testFile+" -bv 7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-error-compulsory-argument-missing.expected");
   // duplicate switch but different argument values
   test_QGetOpt_Process->start(testFile+" --verbose 7 -v 4 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process--duplicate-switch-error.expected");
   // duplicate switch but different argument count
   test_QGetOpt_Process->start(testFile+" --verbose 7 -v test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process--duplicate-switch-error.expected");
   // Note the following is not an error situation, duplicate switch but identical arguments
   test_QGetOpt_Process->start(testFile+" --verbose 7 -v 7 test1.log");
   test_QGetOpt_Process->waitForFinished();
   checkFiles("test_QGetOpt_Process.actual","test_QGetOpt_Process-v7.expected");


   // Remove the actual file
//   QFile::remove("test_QGetOpt_Process.actual");
   generateCoverageFiles();
}


QTEST_MAIN(qgetoptTest)
#include "qgetoptTest.moc"
